
# 从 blinker 库导入 Signal 类，用于实现信号与槽机制
from blinker import Signal
# 导入 json 模块，用于处理 JSON 数据
import json
# 导入 uuid 模块，用于生成唯一标识符
import uuid

# 从 src 模块导入根据引脚类型获取默认值的函数
from src import getPinDefaultValueByType
# 从 src 核心的通用模块导入所有内容
from src.Core.Common import *
# 从 src 核心的接口模块导入 IItemBase 接口
from src.Core.Interfaces import IItemBase


class Variable(IItemBase):
    """
    变量的表示类，封装了变量的各种属性和操作，同时提供信号机制，用于在变量属性变化时通知其他部分。

    :var nameChanged: 当变量名称发生变化时触发的信号
    :vartype nameChanged: :class:`~blinker.base.Signal`
    :var valueChanged: 当变量值发生变化时触发的信号
    :vartype valueChanged: :class:`~blinker.base.Signal`
    :var dataTypeChanged: 当变量数据类型发生变化时触发的信号
    :vartype dataTypeChanged: :class:`~blinker.base.Signal`
    :var structureChanged: 当变量结构发生变化时触发的信号
    :vartype structureChanged: :class:`~blinker.base.Signal`
    :var accessLevelChanged: 当变量访问级别发生变化时触发的信号
    :vartype accessLevelChanged: :class:`~blinker.base.Signal`
    :var killed: 当变量被销毁时触发的信号
    :vartype killed: :class:`~blinker.base.Signal`
    :var graph: 对拥有该变量的图的引用
    :vartype graph: :class:`~PyFlow.Core.GraphBase.GraphBase`
    """

    def __init__(
        self,
        graph,
        value,
        name,
        dataType,
        accessLevel=AccessLevel.public,
        structure=StructureType.Single,
        uid=None,
    ):
        """
        构造函数，初始化变量的各种属性，并设置相应的信号。

        :param graph: 拥有该变量的图
        :type graph: :class:`~PyFlow.Core.GraphBase.GraphBase`
        :param value: 变量的值
        :type value: object
        :param name: 变量的名称
        :type name: str
        :param dataType: 变量的数据类型
        :type dataType: str
        :param accessLevel: 变量的访问级别
        :type accessLevel: :class:`~PyFlow.Core.Common.AccessLevel`
        :param structure: 变量的结构
        :type structure: :attr:`~PyFlow.Core.Common.StructureType.Single`
        :param uid: 变量的唯一标识符
        :type uid: :class:`~uuid.UUID`
        """
        # 调用父类的构造函数
        super(Variable, self).__init__()
        # 定义变量名称改变的信号
        self.nameChanged = Signal(str)
        # 定义变量值改变的信号
        self.valueChanged = Signal(str)
        # 定义变量数据类型改变的信号
        self.dataTypeChanged = Signal(str)
        # 定义变量结构改变的信号
        self.structureChanged = Signal(str)
        # 定义变量访问级别改变的信号
        self.accessLevelChanged = Signal(str)
        # 定义变量被销毁的信号
        self.killed = Signal()

        # 保存对拥有该变量的图的引用
        self.graph = graph

        # 初始化变量的名称
        self._name = name
        # 初始化变量的值
        self._value = value
        # 初始化变量的数据类型
        self._dataType = dataType
        # 初始化变量的结构
        self._structure = structure
        # 初始化变量的访问级别
        self._accessLevel = accessLevel
        # 初始化变量所属包的名称
        self._packageName = None
        # 初始化变量的唯一标识符，如果未提供则自动生成
        self._uid = uuid.uuid4() if uid is None else uid
        # 确保唯一标识符是 uuid.UUID 类型
        assert isinstance(self._uid, uuid.UUID)
        # 更新变量所属包的名称
        self.updatePackageName()
        # 初始化 UI 包装器
        self._uiWrapper = None

    def getWrapper(self):
        """
        获取变量的 UI 包装器。

        :return: 如果存在 UI 包装器则返回，否则返回 None
        """
        if self._uiWrapper is not None:
            return self._uiWrapper()
        return None

    def setWrapper(self, wrapper):
        """
        设置变量的 UI 包装器，仅在包装器为空时设置。

        :param wrapper: UI 包装器对象
        """
        if self._uiWrapper is None:
            self._uiWrapper = weakref.ref(wrapper)

    def location(self):
        """
        返回变量的位置，即拥有该变量的图的位置。

        .. seealso:: :meth:`~PyFlow.Core.GraphBase.GraphBase.location`
        """
        return self.graph.location()

    def findRefs(self):
        """
        返回所有引用该变量的 getVar 和 setVar 节点实例。

        :return: 引用该变量的节点实例列表
        """
        return self.graph.graphManager.findVariableRefs(self)

    def updatePackageName(self):
        """
        根据变量的数据类型更新变量所属包的名称。
        """
        # 假设 findPinClassByType 函数存在于全局作用域中
        self._packageName = findPinClassByType(self._dataType)._packageName

    @property
    def packageName(self):
        """
        获取变量类型所属包的名称。

        :rtype: str
        """
        return self._packageName

    @packageName.setter
    def packageName(self, value):
        """
        设置变量类型所属包的名称，并尝试发送包名改变的信号（该信号目前不存在）。

        :param value: 新的包名
        :type value: str
        """
        # 确保传入的包名是字符串类型
        assert isinstance(value, str)
        self._packageName = value
        # TODO: 此信号不存在，仅单次使用
        self.packageNameChanged.send(value)

    @property
    def accessLevel(self):
        """
        获取变量的访问级别。

        :rtype: :class:`~PyFlow.Core.Common.AccessLevel`
        """
        return self._accessLevel

    @accessLevel.setter
    def accessLevel(self, value):
        """
        设置变量的访问级别，并发送访问级别改变的信号。

        :param value: 新的访问级别
        :type value: :class:`~PyFlow.Core.Common.AccessLevel`
        """
        # 确保传入的访问级别是 AccessLevel 类型
        assert isinstance(value, AccessLevel)
        self._accessLevel = value
        # 发送访问级别改变的信号
        self.accessLevelChanged.send(value)

    @property
    def name(self):
        """
        获取变量的名称。

        :return: 变量的名称
        :rtype: str
        """
        return self._name

    @name.setter
    def name(self, value):
        """
        设置变量的名称，并发送名称改变的信号。

        :param value: 新的变量名称
        :type value: str
        """
        # 确保传入的名称是字符串类型
        assert isinstance(value, str)
        self._name = value
        # 发送名称改变的信号
        self.nameChanged.send(value)

    @property
    def value(self):
        """
        获取变量的值。

        :rtype: object
        """
        return self._value

    @value.setter
    def value(self, value):
        """
        设置变量的值，并在值发生变化时发送值改变的信号。在设置值时会进行类型检查（如果变量不是 AnyPin 类型）。

        :param value: 新的变量值
        :type value: object
        """
        # 如果变量不是 AnyPin 类型，则进行类型检查
        if not self.dataType == "AnyPin":
            # 假设 findPinClassByType 函数存在于全局作用域中
            supportedDataTypes = findPinClassByType(self.dataType).supportedDataTypes()
            if self.dataType not in supportedDataTypes:
                return

        try:
            # 检查值是否发生变化
            if self._value != value or type(self._value) != type(value):
                self._value = value
                # 发送值改变的信号
                self.valueChanged.send(value)
        except:
            # 若出现异常，直接设置值并发送信号
            self._value = value
            self.valueChanged.send(value)

    @property
    def dataType(self):
        """
        获取变量的数据类型。

        :rtype: str
        """
        return self._dataType

    @dataType.setter
    def dataType(self, value):
        """
        设置变量的数据类型，更新所属包的名称，将值设置为该类型的默认值，并发送数据类型改变的信号。

        :param value: 新的数据类型
        :type value: str
        """
        # 确保传入的数据类型是字符串类型
        assert isinstance(value, str)
        if value != self._dataType:
            self._dataType = value
            # 更新变量所属包的名称
            self.updatePackageName()
            # 将变量的值设置为该类型的默认值
            self.value = getPinDefaultValueByType(value)
            # 发送数据类型改变的信号
            self.dataTypeChanged.send(value)

    @property
    def structure(self):
        """
        获取变量的结构。

        :rtype: :class:`~PyFlow.Core.Common.StructureType`
        """
        return self._structure

    @structure.setter
    def structure(self, value):
        """
        设置变量的结构，根据新结构更新变量的值，并发送结构改变的信号。

        :param value: 新的变量结构
        :type value: :class:`~PyFlow.Core.Common.StructureType`
        """
        # 确保传入的结构是 StructureType 类型
        assert isinstance(value, StructureType)
        if value != self._structure:
            self._structure = value
            if self._structure == StructureType.Array:
                # 如果结构是数组，将值设置为列表
                self.value = list()
            if self._structure == StructureType.Dict:
                # 如果结构是字典，将值设置为 PFDict 实例
                self.value = PFDict("IntPin", "BoolPin")
            # 发送结构改变的信号
            self.structureChanged.send(self._structure)

    @property
    def uid(self):
        """
        获取变量的唯一标识符。

        :return: 变量的唯一标识符
        :rtype: :class:`~uuid.UUID`
        """
        return self._uid

    @uid.setter
    def uid(self, value):
        """
        设置变量的唯一标识符，并更新图中变量存储的键。

        :param value: 新的唯一标识符
        :type value: :class:`~uuid.UUID`
        """
        # 确保传入的唯一标识符是 uuid.UUID 类型
        assert isinstance(value, uuid.UUID)
        # 更新图中变量存储的键
        self.graph.getVars()[value] = self.graph.getVars().pop(self._uid)
        self._uid = value

    def serialize(self):
        """
        将变量序列化为字典，用于存储或传输。

        :return: 包含变量信息的字典
        :rtype: dict
        """
        # 假设 findPinClassByType 函数存在于全局作用域中
        pinClass = findPinClassByType(self.dataType)

        # 获取变量序列化的模板
        template = Variable.jsonTemplate()

        # 将唯一标识符转换为字符串
        uidString = str(self.uid)

        # 设置变量的名称
        template["name"] = self.name
        if self.dataType == "AnyPin":
            # 如果数据类型是 AnyPin，值设置为 None
            template["value"] = None
        else:
            # 否则，将值进行 JSON 序列化
            template["value"] = json.dumps(self.value, cls=pinClass.jsonEncoderClass())
        if self.structure == StructureType.Dict:
            # 如果结构是字典，设置字典的键类型和值类型
            template["dictKeyType"] = self.value.keyType
            template["dictValueType"] = self.value.valueType
        else:
            # 否则，设置数据类型
            template["dataType"] = self.dataType
        # 设置变量的结构名称
        template["structure"] = self.structure.name
        # 设置变量的访问级别名称
        template["accessLevel"] = self.accessLevel.name
        # 设置变量所属包的名称
        template["package"] = self._packageName
        # 设置变量的唯一标识符字符串
        template["uuid"] = uidString

        return template

    @staticmethod
    def deserialize(graph, jsonData, *args, **kwargs):
        """
        从序列化的字典数据中反序列化出变量实例。

        :param graph: 拥有该变量的图
        :type graph: :class:`~PyFlow.Core.GraphBase.GraphBase`
        :param jsonData: 包含变量信息的字典
        :type jsonData: dict
        :return: 反序列化后的变量实例
        :rtype: :class:`Variable`
        """
        # 获取变量的名称
        name = jsonData["name"]

        # 初始化数据类型
        dataType = "BoolPin"
        if jsonData["structure"] == StructureType.Dict.name:
            # 如果结构是字典，获取字典的键类型和值类型
            keyDataType = jsonData["dictKeyType"]
            valueDataType = jsonData["dictValueType"]

            # 创建 PFDict 实例作为变量的值
            value = PFDict(keyDataType, valueDataType)
        else:
            # 否则，获取数据类型
            dataType = jsonData["dataType"]

            if dataType != "AnyPin":
                # 假设 findPinClassByType 函数存在于全局作用域中
                pinClass = findPinClassByType(dataType)
                # 对值进行 JSON 反序列化
                value = json.loads(jsonData["value"], cls=pinClass.jsonDecoderClass())
            else:
                # 如果数据类型是 AnyPin，获取 AnyPin 类型的默认值
                value = getPinDefaultValueByType("AnyPin")

        # 获取变量的访问级别
        accessLevel = AccessLevel[jsonData["accessLevel"]]
        # 获取变量的结构
        structure = StructureType[jsonData["structure"]]
        # 获取变量的唯一标识符
        uid = uuid.UUID(jsonData["uuid"])
        # 创建并返回变量实例
        return Variable(graph, value, name, dataType, accessLevel, structure, uid)

    @staticmethod
    def jsonTemplate():
        """
        返回一个包含序列化所需最小字段的字典模板。

        :rtype: dict
        """
        template = {
            "name": None,
            "value": None,
            "dataType": None,
            "accessLevel": None,
            "package": None,
            "uuid": None,
        }
        return template